﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HybridCLR;
using Obfuz;
using Obfuz.EncryptionVM;
using UnityEngine;
using YooAsset;

public class Boot : MonoBehaviour
{
    [Header("YooAsset Settings")] public EPlayMode playMode = EPlayMode.EditorSimulateMode;
    public string packageName = "DefaultPackage";

    [Header("Default Server Settings (Fallback)")] [SerializeField] [Tooltip("当本地和缓存配置都加载失败时，使用的备用CDN地址")]
    public string fallbackCdnUrl = "http://127.0.0.1:8888";

    [SerializeField] [Tooltip("当本地和缓存配置都加载失败时，使用的备用版本号")]
    public string fallbackAppVersion = "v1.0";


    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
        Application.targetFrameRate = 60;
        Application.runInBackground = true;

        InitPatchWindow();
        StartCoroutine(GameStartup());
    }

    private void InitPatchWindow()
    {
        var desktop = GameObject.Find("UI");
        for (int i = desktop.transform.childCount - 1; i >= 0; i--)
        {
            var child = desktop.transform.GetChild(i);
            GameObject.Destroy(child.gameObject);
        }
        var prefab = Resources.Load<GameObject>("Prefabs/PatchWindow");
        Instantiate(prefab, desktop.transform);
    }

    private IEnumerator GameStartup()
    {
        PatchManager.Instance.Behaviour = this;

        Debug.Log("--- 游戏启动流程开始 ---");
        YooAssets.Initialize();

        var operation = new PatchOperation(packageName, playMode);
        YooAssets.StartOperation(operation);
        yield return operation;

        // 设置默认的资源包
        var gamePackage = YooAssets.GetPackage(packageName);
        YooAssets.SetDefaultPackage(gamePackage);

        // 切换到Loading主页面
        // operation.LoadDll();
        LoadDll();

        // SceneEventDefine.ChangeToHomeScene.SendEventMessage();
    }

    public void LoadDll()
    {
        Debug.Log("---- Load Dll ----");
// #if !UNITY_EDITOR
        LoadMetadataForAOTAssemblies();
        // var handle = YooAssets.LoadAssetSync<TextAsset>("HotUpdate.dll");
        // var text = handle.AssetObject as TextAsset;
        // _hotUpdateAss = Assembly.Load(text?.bytes);
// #else
//         _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");
// #endif

        // Type entryType = _hotUpdateAss.GetType("Entry");
        // entryType.GetMethod("Start").Invoke(null, null);
        // Type entryType = _hotUpdateAss.GetType("Entry");
        // IEntry entry = (IEntry)Activator.CreateInstance(entryType);
        // entry.Start();
        
        StartCoroutine(Run_InstantiateComponentByAsset());
    }

    private static Assembly _hotUpdateAss;

    private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>()
    {
        "mscorlib.dll",
        "System.dll",
        "System.Core.dll",
    };

    /// <summary>
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    private void LoadMetadataForAOTAssemblies()
    {
        /// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
        /// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
        /// 
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyFiles)
        {
            var handle = YooAssets.LoadAssetSync<TextAsset>($"{aotDllName}");
            var text = handle.AssetObject as TextAsset;
            byte[] dllBytes = text?.bytes;
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }
    
    // 初始化EncryptionService后被混淆的代码才能正常运行，
    // 因此尽可能地早地初始化它。
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    private static void SetUpStaticSecretKey()
    {
        Debug.Log("SetUpStaticSecret begin");
        EncryptionService<DefaultStaticEncryptionScope>.Encryptor = 
            new GeneratedEncryptionVirtualMachine(Resources.Load<TextAsset>("Obfuz/defaultStaticSecretKey").bytes);
        Debug.Log("SetUpStaticSecret end");
    }
    
    private static IEnumerator Run_InstantiateComponentByAsset()
    {
        // 通过实例化assetbundle中的资源，还原资源上的热更新脚本
        var handle = YooAssets.LoadAssetAsync<GameObject>("MainController");
        yield return handle;
        
        var desktop = GameObject.Find("UI");
        for (int i = desktop.transform.childCount - 1; i >= 0; i--)
        {
            var child = desktop.transform.GetChild(i);
            GameObject.Destroy(child.gameObject);
        }
        // UniWindow.Initalize(canvas);
        // UniWindow.OpenWindowSync<>()
        Instantiate(handle.AssetObject as GameObject);

    }
}